#include <string>
#include <chrono>
#include <map>
#include <vector>
#include <iostream>
#include <ctime>
#include <cassert>
#include <stdlib.h>
#include <algorithm>
#include <iomanip>
#include <ctime>
#include <sstream>

constexpr int APPROX_BDAYS_PER_MONTH = 21;
constexpr int APPROX_BDAYS_PER_YEAR = 252;

constexpr int MONTHS_PER_YEAR = 12;
constexpr int WEEKS_PER_YEAR = 52;
constexpr int QTRS_PER_YEAR = 4;

std::string DAILY = "daily";
std::string WEEKLY = "weekly";
std::string MONTHLY = "monthly";
std::string QUARTERLY = "quarterly";
std::string YEARLY = "yearly";

std::map<std::string, int> AnnualizationFactors = {
        {"yearly", MONTHS_PER_YEAR},
        {"quarterly", QTRS_PER_YEAR},
        {"monthly", MONTHS_PER_YEAR},
        {"weekly", WEEKS_PER_YEAR},
        {"daily", APPROX_BDAYS_PER_YEAR}
    };

struct DataFrame{
    std::vector<double> index;
    std::vector<std::string> columns;
    std::vector<std::vector<double>> dataframe;
};

struct Series{
    std::vector<std::string> index;
    std::vector<double> series;
};

namespace empyrical {
    
    std::chrono::system_clock::time_point getUtcTimestamp(double timestamp);

}

// Function to convert timestamp to UTC time point
std::chrono::system_clock::time_point getUtcTimestamp(double timestamp) {
    // Assuming timestamp is in seconds, you can use milliseconds if needed
    std::chrono::duration<double> duration(timestamp);
    std::chrono::system_clock::time_point utcTime = std::chrono::system_clock::from_time_t(0) + + std::chrono::duration_cast<std::chrono::seconds>(duration);;

    return utcTime;
}

std::vector<double> _adjust_returns(const std::vector<double> &returns, double adjustment_factor){
    /*
    Returns the returns array adjusted by adjustment_factor. Optimizes for the
    case of adjustment_factor being 0 by returning returns itself, not a copy!

    Parameters
    ----------
    returns : std::vector<double>
    adjustment_factor : double

    Returns
    -------
    adjusted_returns : std::vector<double>
    */
    if (adjustment_factor == 0.0){
        return returns;
    }
    else 
    {
        std::size_t num = returns.size();
        std::vector<double> adjust_returns(num);
        for (std::size_t i=0;i<num;i++){
            adjust_returns[i] = returns[i] -adjustment_factor;
        }
        
        return adjust_returns;
    }

}

double annualization_factor(std::string period, double annualization){
    /*
    Return annualization factor from period entered or if a custom
    value is passed in.

    Parameters
    ----------
    period : std::string, optional
        Defines the periodicity of the 'returns' data for purposes of
        annualizing. Value ignored if `annualization` parameter is specified.
        Defaults are::

            'monthly':12
            'weekly': 52
            'daily': 252

    annualization : double, optional
        Used to suppress default values available in `period` to convert
        returns into annual returns. Value should be the annual frequency of
        `returns`.

    Returns
    -------
    annualization_factor : double
    */
    if (annualization == 0) {
        // Check if 'annualization' parameter is specified
        auto it = AnnualizationFactors.find(period);
        if (it != AnnualizationFactors.end()) {
            return static_cast<float>(it->second);
        } else {
            throw std::invalid_argument("Invalid 'period' parameter.");
        }
    } else {
        return static_cast<float>(annualization);
    }
    }


std::vector<double> simpleReturns(const std::vector<double>& prices) {
    int numRows = prices.size();
    if (numRows <= 1){
        std::cout << "simpleReturns input param prices length less 2" << std::endl;
        assert(numRows <= 1);
        //throw("simpleReturns input param prices length less 2");
        //std::abort("simpleReturns input param prices length less 2");
    }
    
    std::vector<double> returns(numRows-1);
    for (std::size_t  i = 1; i < numRows; i++) {
        returns[i - 1] = (prices[i] - prices[i - 1]) / prices[i - 1];
    }
    //std::cout << "simpleReturns calculate success" << std::endl;
    
    return returns;
}


DataFrame simpleReturns( DataFrame & prices_df) {
    /*
    calculate the simple return of a dataframe-like data 

    Parameters
    ----------
    prices: DataFrame, dataframe-like data
    
    Returns
    -------
    std::vector<std::vector<double>>
    */
    DataFrame df;
    std::vector<std::vector<double>> returns;
    std::vector<double> new_index(prices_df.index.begin()+1,prices_df.index.end());
    df.columns = prices_df.columns;
    df.index = new_index;
    df.dataframe = returns;
    // if vector is empty, just return the initial returns;
    if (prices_df.dataframe.empty()){
        return df;
    }
    // if vector is not empty 
    int numRows = prices_df.dataframe.size();
    int numCols = prices_df.dataframe[0].size();

    if (numRows > 1) {
        returns.resize(numRows - 1, std::vector<double>(numCols, 0.0));

        for (int i = 1; i < numRows; i++) {
            for (int j = 0; j < numCols; j++) {
                returns[i - 1][j] = (prices_df.dataframe[i][j] - prices_df.dataframe[i - 1][j]) / prices_df.dataframe[i - 1][j];
            }
        }
        df.dataframe = returns;
        return df;
    }
    else{
        return df;
    }
}



DataFrame cum_returns(const DataFrame & returns_df, double startingValue = 0) {
    /*
    calculate the cumulative return of a dataframe-like data 

    Parameters
    ----------
    returns_df: DataFrame, dataframe-like data
    
    Returns
    -------
    DataFrame: cum_returns_df
    */
    std::vector<std::vector<double>> cumulativeReturns;
    DataFrame cum_returns_df;
    cum_returns_df.index = returns_df.index;
    cum_returns_df.columns = returns_df.columns;
    cum_returns_df.dataframe = cumulativeReturns;
    std::vector<std::vector<double>> returns = returns_df.dataframe;
    if (returns.empty()) {
        return cum_returns_df;
    }

    int numRows = returns.size();
    int numCols = returns[0].size();
    cumulativeReturns.resize(numRows, std::vector<double>(numCols, 0.0));

    // 处理第一行数据
    for (int j = 0; j < numCols; j++) {
        
        if (startingValue!=0){
            cumulativeReturns[0][j] = (1.0 + returns[0][j])*startingValue;
        }
        else{
            cumulativeReturns[0][j] = (1.0 + returns[0][j]);
        }
    }
    // 处理第二行到numRows的数据
    for (int j = 0; j < numCols; j++) {
        for (int i = 1; i < numRows; i++) {
            cumulativeReturns[i][j] = cumulativeReturns[i - 1][j] * (1.0 + returns[i][j]);
        }
    }
    // 循环，把所有的值减去1
    for (int j = 0; j < numCols; j++) {
        for (int i = 0; i < numRows; i++) {
            cumulativeReturns[i][j] = cumulativeReturns[i][j] - 1;
        }
    }
    // 赋值
    cum_returns_df.dataframe = cumulativeReturns;
    return cum_returns_df;
}

DataFrame cum_returns_final(const DataFrame & returns_df, double startingValue = 0){
    /*
    calculate the final nav of a dataframe-like data 

    Parameters
    ----------
    returns_df: DataFrame, dataframe-like data
    
    Returns
    -------
    DataFrame: cum_returns_df
    */
    std::vector<std::vector<double>> cumulativeReturns;
    DataFrame cum_returns_df;
    cum_returns_df.index = returns_df.index;
    cum_returns_df.columns = returns_df.columns;
    cum_returns_df.dataframe = cumulativeReturns;
    std::vector<std::vector<double>> returns = returns_df.dataframe;
    if (returns.empty()) {
        return cum_returns_df;
    }

    int numRows = returns.size();
    int numCols = returns[0].size();
    cumulativeReturns.resize(numRows, std::vector<double>(numCols, 0.0));

    // 处理第一行数据
    for (int j = 0; j < numCols; j++) {
        
        if (startingValue!=0){
            cumulativeReturns[0][j] = (1.0 + returns[0][j])*startingValue;
        }
        else{
            cumulativeReturns[0][j] = (1.0 + returns[0][j]);
        }
    }
    // 处理第二行到numRows的数据
    for (int j = 0; j < numCols; j++) {
        for (int i = 1; i < numRows; i++) {
            cumulativeReturns[i][j] = cumulativeReturns[i - 1][j] * (1.0 + returns[i][j]);
        }
    }
    // 赋值
    cum_returns_df.dataframe = cumulativeReturns;
    return cum_returns_df;
}
    

void printVector(const std::vector<double> v){
    std::size_t num = v.size();
    // std::cout << "vector has " << num << " num" << std::endl;
    for (std::size_t i=0;i<num;i++){
        std::cout << v[i] << " ";
    }
    std::cout << std::endl;
}

void printVector(const std::vector<std::string> v){
    std::size_t num = v.size();
    // std::cout << "vector has " << num << " num" << std::endl;
    for (std::size_t i=0;i<num;i++){
        std::cout << v[i] << " ";
    }
    std::cout << std::endl;
}

void printDataFrame(const DataFrame & df){
    std::size_t num = df.index.size();
    std::cout << " ";
    printVector(df.columns);
    for (std::size_t i = 0; i< num; i++){
        std::cout << df.index[i] << " ";
        printVector(df.dataframe[i]);
    }
}

// 获取当前时间的纳秒时间戳
long long getCurrentNanoTimestamp() {
    auto now = std::chrono::high_resolution_clock::now();
    return std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
}

// 将纳秒时间戳转换为带纳秒的字符串时间
std::string nanoTimestampToString(long long nanoTimestamp) {
    auto seconds = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::nanoseconds(nanoTimestamp));
    auto nanoseconds = std::chrono::nanoseconds(nanoTimestamp) - seconds;

    time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::time_point(seconds));
    struct tm *tm = std::localtime(&time);

    if (tm) {
        char buffer[80];
        strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm);
        std::ostringstream oss;
        oss << buffer << " " << nanoseconds.count(); // Append nanoseconds
        return oss.str();
    }

    return "Invalid Time"; // Handle error case
}

// 将字符串时间转换为纳秒时间戳
long long stringToNanoTimestamp(const std::string& timeString) {
    std::tm tm = {};
    std::istringstream iss(timeString);
    iss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");

    auto timePoint = std::chrono::system_clock::from_time_t(std::mktime(&tm));
    return std::chrono::duration_cast<std::chrono::nanoseconds>(timePoint.time_since_epoch()).count();
}

// Function to aggregate returns by week, month, or year
// std::map<std::string, double> aggregateReturns(const DataFrame & returns, const std::string& convertTo) {
//     std::map<std::string, double> aggregatedReturns;
//     DataFrame returns_df = cum_returns(returns);

//     if (convertTo == "weekly") {
//         // Aggregate by week
//         for (const auto& pair : returns) {
//             std::string week = pair.first.substr(0, 7); // Extract the year and month
//             aggregatedReturns[week] += pair.second;
//         }
//     } else if (convertTo == "monthly") {
//         // Aggregate by month
//         for (const auto& pair : returns) {
//             std::string month = pair.first.substr(0, 4); // Extract the year
//             aggregatedReturns[month] += pair.second;
//         }
//     } else if (convertTo == "yearly") {
//         // Aggregate by year
//         for (const auto& pair : returns) {
//             std::string year = pair.first.substr(0, 4);
//             aggregatedReturns[year] += pair.second;
//         }
//     } else {
//         throw std::invalid_argument("Invalid 'convertTo' parameter.");
//     }

//     return aggregatedReturns;
// }









